    // Copyright (C) 2003  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_DIR_NAV_KERNEL_1_CPp_
    #define DLIB_DIR_NAV_KERNEL_1_CPp_
    #include "../platform.h"

    #ifdef WIN32

    #include "dir_nav_kernel_1.h"
    #include "../string.h"

    #ifdef __BORLANDC__
    // Apparently the borland compiler doesn't define this.
    #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
    #endif

    namespace dlib
    {

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // file object implementation
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        void file::
        init (
            const std::string& name
        )
        {
            using namespace std;

            char buf[3000];
            char* str;
            if (GetFullPathNameA(name.c_str(),sizeof(buf),buf,&str) == 0)
            {
                // the file was not found
                throw file_not_found("Unable to find file " + name);
            }
            state.full_name = buf;

            string::size_type pos = state.full_name.find_last_of(directory::get_separator());
            if (pos == string::npos)
            {
                // no valid full path has no separator characters.
                throw file_not_found("Unable to find file " + name);
            }
            state.name = state.full_name.substr(pos+1);

            // now find the size of this file
            WIN32_FIND_DATAA data;
            HANDLE ffind = FindFirstFileA(state.full_name.c_str(), &data);
            if (ffind == INVALID_HANDLE_VALUE ||
                (data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) != 0)
            {
                throw file_not_found("Unable to find file " + name);
            }
            else
            {
                uint64 temp = data.nFileSizeHigh;
                temp <<= 32;
                temp |= data.nFileSizeLow;
                state.file_size = temp;
                FindClose(ffind);

                ULARGE_INTEGER ull;
                ull.LowPart = data.ftLastWriteTime.dwLowDateTime;
                ull.HighPart = data.ftLastWriteTime.dwHighDateTime;
                std::chrono::nanoseconds epoch(100 * (ull.QuadPart - 116444736000000000));
                state.last_modified = std::chrono::time_point<std::chrono::system_clock>(std::chrono::duration_cast<std::chrono::system_clock::duration>(epoch));
            }

        }

    // ----------------------------------------------------------------------------------------

        bool file::
        operator == (
            const file& rhs
        ) const
        {
            using namespace std;

            if (state.full_name.size() != rhs.state.full_name.size())
                return false;

            // compare the strings but ignore the case because file names
            // are not case sensitive on windows
            return tolower(state.full_name) == tolower(rhs.state.full_name);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // directory object implementation
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        void directory::
        init (
            const std::string& name
        )
        {
            using namespace std;

            char buf[3000];
            char* str;
            if (GetFullPathNameA(name.c_str(),sizeof(buf),buf,&str) == 0)
            {
                // the directory was not found
                throw dir_not_found("Unable to find directory " + name);
            }
            state.full_name = buf;

            const char sep = get_separator();
            if (is_root_path(state.full_name) == false)
            {
                // ensure that thre is not a trialing separator
                if (state.full_name[state.full_name.size()-1] == sep)
                    state.full_name.erase(state.full_name.size()-1);

                // pick out the directory name
                string::size_type pos = state.full_name.find_last_of(sep);
                state.name = state.full_name.substr(pos+1);
            }
            else
            {
                // ensure that there is a trailing separator
                if (state.full_name[state.full_name.size()-1] != sep)
                    state.full_name += sep;
            }

            // now check that this is actually a valid directory
            DWORD attribs = GetFileAttributesA(state.full_name.c_str());
            if (attribs == INVALID_FILE_ATTRIBUTES ||
                (attribs&FILE_ATTRIBUTE_DIRECTORY) == 0)
            {
                // the directory was not found
                throw dir_not_found("Unable to find directory " + name);
            }

        }

    // ----------------------------------------------------------------------------------------

        char directory::
        get_separator (
        )
        {
            return '\\';
        }

    // ----------------------------------------------------------------------------------------

        bool directory::
        operator == (
            const directory& rhs
        ) const
        {
            using namespace std;

            if (state.full_name.size() != rhs.state.full_name.size())
                return false;

            // compare the strings but ignore the case because file names
            // are not case sensitive on windows
            return tolower(state.full_name) == tolower(rhs.state.full_name);
        }

    // ----------------------------------------------------------------------------------------

        const directory directory::
        get_parent (
        ) const
        {
            using namespace std;
            // if *this is the root then just return *this
            if (is_root())
            {
                return *this;
            }
            else
            {
                directory temp;

                const char sep = get_separator();

                string::size_type pos = state.full_name.find_last_of(sep);
                temp.state.full_name = state.full_name.substr(0,pos);

                if ( is_root_path(temp.state.full_name))
                {
                    temp.state.full_name += sep;
                }
                else
                {
                    pos = temp.state.full_name.find_last_of(sep);
                    if (pos != string::npos)
                    {
                        temp.state.name = temp.state.full_name.substr(pos+1);
                    }
                    else
                    {
                        temp.state.full_name += sep;
                    }
                }
                return temp;
            }
        }

    // ----------------------------------------------------------------------------------------

        bool directory::
        is_root_path (
            const std::string& path
        ) const
        {
            using namespace std;
            const char sep = get_separator();
            bool root_path = false;
            if (path.size() > 2 && path[0] == sep && path[1] == sep)
            {
                // in this case this is a windows share path
                string::size_type pos = path.find_first_of(sep,2);
                if (pos != string::npos)
                {
                    pos = path.find_first_of(sep,pos+1);

                    if (pos == string::npos && path[path.size()-1] != sep)
                        root_path = true;
                    else if (pos == path.size()-1)
                        root_path = true;
                }

            }
            else if ( (path.size() == 2 || path.size() == 3) && path[1] == ':')
            {
                // if this is a valid windows path then it must be a root path
                root_path = true;
            }

            return root_path;
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // WIN32

    #endif // DLIB_DIR_NAV_KERNEL_1_CPp_